package com.feature.shop.core.security;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.PatternMatchUtils;

import com.feature.core.constant.SecurityConstants;
import com.feature.core.security.Logical;
import com.feature.core.security.SecurityContextHolder;
import com.feature.core.security.annotation.RequiresPermissions;
import com.feature.core.security.annotation.RequiresRoles;
import com.feature.core.util.AssertUtil;
import com.feature.shop.auth.api.vo.LoginUserVo;

/**
 * 基于 Spring Aop 的注解鉴权
 */
@Aspect
public class PreAuthorizeAspect {
    private static final String SUPER_ADMIN = "管理员";

    /**
     * 构建
     */
    public PreAuthorizeAspect() {
    }

    /**
     * 定义AOP签名 (切入所有使用鉴权注解的方法)
     */
    public static final String POINTCUT_SIGN = "@annotation(com.feature.core.security.annotation.RequiresPermissions) || " + "@annotation(com.feature.core.security.annotation.RequiresRoles)";

    /**
     * 声明AOP签名
     */
    @Pointcut(POINTCUT_SIGN)
    public void pointcut() {
    }

    /**
     * 环绕切入
     * 
     * @param joinPoint 切面对象
     * @return 底层方法执行后的返回值
     * @throws Throwable 底层方法抛出的异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 注解鉴权
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        checkMethodAnnotation(signature.getMethod());
        try {
            // 执行原有逻辑
            Object obj = joinPoint.proceed();
            return obj;
        } catch (Throwable e) {
            throw e;
        }
    }

    /**
     * 对一个Method对象进行注解检查
     */
    public void checkMethodAnnotation(Method method) {

        LoginUserVo loginUserVo = SecurityContextHolder.get(SecurityConstants.LOGIN_USER_KEY);
        if (loginUserVo != null) {
            // 校验 @RequiresRoles 注解
            RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);
            if (requiresRoles != null) {
                Set<String> roles = loginUserVo.getUser().getRoles();
                if (requiresRoles.logical().equals(Logical.AND)) {
                    long count = Stream.of(requiresRoles.value()).takeWhile(v -> hasRole(roles, v)).count();
                    AssertUtil.isTrue(count == requiresRoles.value().length, "无权访问");
                } else if (requiresRoles.logical().equals(Logical.OR)) {
                    long count = Stream.of(requiresRoles.value()).dropWhile(v -> !hasRole(roles, v)).count();
                    AssertUtil.isTrue(count == 0, "无权访问");
                }

            }
            // 校验 @RequiresPermissions 注解
            RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
            if (requiresPermissions != null) {
                boolean isAdmin = loginUserVo.getUser().getRoles().stream().filter(v -> v.equals(SUPER_ADMIN)).count() > 0;
                if (!isAdmin) {
                    List<String> auths = loginUserVo.getUser().getAuths().stream().collect(Collectors.toList());
                    if (requiresPermissions.logical().equals(Logical.AND)) {
                        long count = Stream.of(requiresPermissions.value()).takeWhile(v -> hasPermi(auths, v)).count();
                        AssertUtil.isTrue(count == requiresPermissions.value().length, "无权访问");
                    } else if (requiresPermissions.logical().equals(Logical.OR)) {
                        long count = Stream.of(requiresPermissions.value()).dropWhile(v -> !hasRole(auths, v)).count();
                        AssertUtil.isTrue(count == 0, "无权访问");
                    }
                }
            }
        }
    }

    /**
     * 判断是否包含权限
     * 
     * @param authorities 权限列表
     * @param permission  权限字符串
     * @return 用户是否具备某权限
     */
    private boolean hasPermi(Collection<String> authorities, String permission) {
        return authorities.stream().filter(StringUtils::isNotBlank).anyMatch(x -> PatternMatchUtils.simpleMatch(x, permission));
    }

    /**
     * 判断是否包含角色
     * 
     * @param roles 角色列表
     * @param role  角色
     * @return 用户是否具备某角色权限
     */
    private boolean hasRole(Collection<String> roles, String role) {
        return roles.stream().filter(StringUtils::isNotBlank).anyMatch(x -> SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role));
    }
}
